package parser.news;

import entities.Channel;
import entities.News;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Abstract class for create handler and parsing news RSS
 * User: kaiser
 * Date: 8/3/13
 * Time: 3:21 AM
 */
public abstract class NewsHandler extends DefaultHandler {

    /**
     * Count of news to parse from resource
     */
    protected int limit;

    /**
     * List of parced news
     */
    protected List<News> newsList = new ArrayList<>();

    /**
     * temporary storage wile news parsing in progress
     */
    protected News news;

    /**
     * resource channel entity, resided from XML
     */
    protected Channel channel;

    /**
     * flag for indicate current news title tag
     */
    protected boolean isNewsTitle = false;

    /**
     * flag for indicate current news URL tag
     */
    protected boolean isNewsUrl = false;

    /**
     * flag for indicate current news publish date tag
     */
    protected boolean isNewsPubDate = false;

    /**
     * flag for indicate current channel name tag
     */
    protected boolean isChannelName = false;

    /**
     * flag for indicate current channel url tag
     */
    protected boolean isChannelUrl = false;

    public NewsHandler() {
        super();
    }

    public NewsHandler(int limit) {
        setLimit(limit);
    }

    /**
     * getting news tag name, each handler has its own
     *
     * @return name of news tag
     */
    abstract String getNewsTag();

    /**
     * getting news title tag name, each handler has its own
     *
     * @return name of news title tag
     */
    abstract String getNewsTitleTag();

    /**
     * getting news url tag name, each handler has its own
     *
     * @return name of news url tag
     */
    abstract String getNewsUrlTag();

    /**
     * getting news publish date tag name, each handler has its own
     *
     * @return name of news publish date tag
     */
    abstract String getNewsPublishDateTag();

    /**
     * getting channel ID  each handler has its own
     *
     * @return channel primary id name
     */
    abstract String getChannelId();

    /**
     * getting channel tag name
     *
     * @return name of channel tag
     */
    protected String getChannelTag() {
        return "image";
    }

    /**
     * getting channel name(title) tag
     *
     * @return name of channel name(title) tag
     */
    protected String getChannelNameTag() {
        return "title";
    }

    /**
     * getting channel URL name
     *
     * @return name of channel URL tag
     */
    protected String getChannelUrlTag() {
        return "link";
    }

    /* Receive notification of the start of an element. */

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes)
            throws SAXException {
        if (isChannelTagName(qName)) {
            Channel channel = new Channel();
            channel.setId(getChannelId());
            setChannel(channel);
        } else if (isChannelNameTagName(qName)) {
            isChannelName = true;
        } else if (isChannelURLTagName(qName)) {
            isChannelUrl = true;
        } else if (isNewsTagName(qName)) {
            /* create new entity in each news tag start */
            setNews(new News());

            /* init news storage field  */
            if (getNewsList() == null) {
                setNewsList(new ArrayList<News>());
            }
        } else if (isNewsTitleTagName(qName)) {
            isNewsTitle = true;
        } else if (isNewsUrlTagName(qName)) {
            isNewsUrl = true;
        } else if (isNewsDateTagName(qName)) {
            isNewsPubDate = true;
        }
    }

    /* Receive notification of character data inside an element. */
    @Override
    public void characters(char ch[], int start, int length) throws SAXException {

        if (isChannelName) {
            getChannel().setName(new String(ch, start, length));
            isChannelName = false;
        } else if (isChannelUrl) {
            getChannel().setLink(new String(ch, start, length));
            isChannelUrl = false;
        } else if (isNewsTitle) {
            getNews().setTitle(new String(ch, start, length));
            isNewsTitle = false;
        } else if (isNewsUrl) {
            getNews().setUrl(new String(ch, start, length));
            isNewsUrl = false;
        } else if (isNewsPubDate) {
            getNews().setPublishedDate(new Date(new String(ch, start, length)));
            isNewsPubDate = false;
        }
    }

    /* Receive notification of the end of an element. */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (isNewsTagName(qName)) {
            /**
             * add current news entity to list
             */
            addNewsToList(getNews());
        }
    }

    /**
     * Check is channel tag name<br/>
     * since it earlier in XML structure news is Null
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is channel tag name
     */
    protected boolean isChannelTagName(String qName) {
        return qName.equalsIgnoreCase(getChannelTag()) && getNews() == null;
    }

    /**
     * Check is channel name(title)tag name<br/>
     * since it earlier in XML structure news is Null
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is channel name tag start
     */
    protected boolean isChannelNameTagName(String qName) {
        return qName.equalsIgnoreCase(getChannelNameTag()) && getChannel() != null && getNews() == null;
    }

    /**
     * Check is channel url tag name start<br/>
     * since it ater in XML structure Channel is not Null
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is channel URL tag start
     */
    protected boolean isChannelURLTagName(String qName) {
        return qName.equalsIgnoreCase(getChannelUrlTag()) && getChannel() != null && getNews() == null;
    }


    /**
     * Check is news name start<br/>
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is news  tag start
     */
    protected boolean isNewsTagName(String qName) {
        return qName.equalsIgnoreCase(getNewsTag());
    }

    /**
     * Check is news title tag name start<br/>
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is news title tag start
     */
    protected boolean isNewsTitleTagName(String qName) {
        return qName.equalsIgnoreCase(getNewsTitleTag()) && getNews() != null;
    }

    /**
     * Check is news url tag name start<br/>
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is news url tag start
     */
    protected boolean isNewsUrlTagName(String qName) {
        return qName.equalsIgnoreCase(getNewsUrlTag()) && getNews() != null;
    }

    /**
     * Check is news publish date tag name start<br/>
     *
     * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.
     * @return true if is news publish date tag start
     */
    protected boolean isNewsDateTagName(String qName) {
        return qName.equalsIgnoreCase(getNewsPublishDateTag()) && getNews() != null;
    }

    public List<News> getNewsList() {
        return newsList;
    }

    News getNews() {
        return news;
    }

    void setNews(News news) {
        this.news = news;
    }

    int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    void addNewsToList(News news) {
        if (getNewsList().size() < getLimit()) {
            news.setChannel(getChannel());
            getNewsList().add(news);
        }
    }

    void setNewsList(List<News> newsList) {
        this.newsList = newsList;
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }


}